#pragma once
#include <iostream>
#include <vector>
#include <semaphore.h>
#include <pthread.h>
using namespace std;
//定义一个默认大小
const static int default_cap = 5;


//单生产 单消费 
// template<class T>
// class  RingQueue
// {
// private:
//     void P(sem_t& sem)
//     {
//         sem_wait(&sem);
//     }
//     void V(sem_t& sem)
//     {
//         sem_post(&sem);
//     }
    
// public:
//     //全部满了之后也没有关系 因为信号量的关系，只有消费者能拿到

//     RingQueue(int cap = default_cap)
//     :_ringqueue(cap) , _cap(cap), _c_step(0), _p_step(0)
//     {
//         //地址， 是否线程间共享，  信号量的初始值
//         sem_init(&_cdata_sem, 0, 0);
//         sem_init(&_pspace_sem, 0, _cap);
//     }

//     void Push(const T& in) //生产数据
//     {
//         //要生产 就要先申请信号量资源
//         //单生产单消费
//         P(_pspace_sem);
//         _ringqueue[_p_step] = in;
//         V(_cdata_sem);

//         //位置后移，并且保持环形队列不越界  可以放到 V 上面
//         _p_step++;
//         _p_step %= _cap;
//     }

//     //输出型参数  之间把 Out 向外带出去
//     void Pop(T* out)  //消费数据
//     {
//         //消费者先申请 数据资源  要先能申请到
//         P(_cdata_sem);
//         *out = _ringqueue[_c_step];
//         V(_pspace_sem);

//         _c_step++;
//         _c_step %= _cap;
//     }

//     ~RingQueue()
//     {
//         sem_destroy(&_cdata_sem);
//         sem_destroy(&_pspace_sem);
//     }



// private:
//     //环形队列也是用数组来模拟的
//     vector<T> _ringqueue;
//     int _cap; //环形队列 容量大小

//     int _c_step;    //消费者下标
//     int _p_step;    //生产者者下标

//     sem_t _cdata_sem;   //消费者关注的数据资源
//     sem_t _pspace_sem;   //生产者关注的空间资源

// };



//多生产 多消费 改造
//生产者和消费者之间的同步互斥关系已经 用信号量实现了
//多生产多消费 要维持生产者之间的互斥， 和消费者之间的互斥 
template<class T>
class  RingQueue
{
private:
    void P(sem_t& sem)
    {
        sem_wait(&sem);
    }
    void V(sem_t& sem)
    {
        sem_post(&sem);
    }
    
    //封装加解锁操作
    void Lock(pthread_mutex_t & mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    void Unlock(pthread_mutex_t & mutex)
    {
        pthread_mutex_unlock(&mutex);

    }

public:
    //全部满了之后也没有关系 因为信号量的关系，只有消费者能拿到

    RingQueue(int cap = default_cap)
    :_ringqueue(cap) , _cap(cap), _c_step(0), _p_step(0)
    {
        //地址， 是否线程间共享，  信号量的初始值
        sem_init(&_cdata_sem, 0, 0);
        sem_init(&_pspace_sem, 0, _cap);

        pthread_mutex_init(&_c_mutex, nullptr);
        pthread_mutex_init(&_p_mutex, nullptr);
    }

    void Push(const T& in) //生产数据
    {
        /*那种更好？  先加锁再申请信号量  还是先申请信号量再加锁  肯定是后者*/
        //信号量是 不需要被保护的，是原子的  
        //多线程在申请资源的时候 可以并行  可以提高并发度
        P(_pspace_sem); 
        Lock(_p_mutex);

        //生产者加锁
        // Lock(_p_mutex);
        // P(_pspace_sem);         //要生产 就要先申请信号量资源

        _ringqueue[_p_step] = in;
        _p_step++;              //位置后移，并且保持环形队列不越界  可以放到 V 上面
        _p_step %= _cap;

        Unlock(_p_mutex);
        V(_cdata_sem);
    }

    //输出型参数  之间把 Out 向外带出去
    void Pop(T* out)  //消费数据
    {
        //消费者先申请 数据资源  要先能申请到
        P(_cdata_sem);
        Lock(_c_mutex);

        *out = _ringqueue[_c_step];
        _c_step++;
        _c_step %= _cap;

        Unlock(_c_mutex);
        V(_pspace_sem);

    }

    ~RingQueue()
    {
        sem_destroy(&_cdata_sem);
        sem_destroy(&_pspace_sem);

        pthread_mutex_destroy(&_c_mutex);
        pthread_mutex_destroy(&_p_mutex);
    }



private:
    //环形队列也是用数组来模拟的
    vector<T> _ringqueue;
    int _cap; //环形队列 容量大小

    int _c_step;    //消费者下标
    int _p_step;    //生产者者下标

    sem_t _cdata_sem;   //消费者关注的数据资源
    sem_t _pspace_sem;   //生产者关注的空间资源

    //需要用两把锁
    pthread_mutex_t _c_mutex;
    pthread_mutex_t _p_mutex;
};

// template<class T>
// class RingQueue{
// private:
//     void P(sem_t &sem)
//     {
//         sem_wait(&sem);
//     }
//     void V(sem_t &sem)
//     {
//         sem_post(&sem);
//     }
//     void Lock(pthread_mutex_t &mutex)
//     {
//         pthread_mutex_lock(&mutex);
//     }
//     void Unlock(pthread_mutex_t &mutex)
//     {
//         pthread_mutex_unlock(&mutex);
//     }
// public:
//     RingQueue(int cap = default_cap)
//     :ringqueue_(cap), cap_(cap), c_step_(0), p_step_(0)
//     {
//         sem_init(&cdata_sem_, 0, 0);
//         sem_init(&pspace_sem_, 0, cap);

//         pthread_mutex_init(&c_mutex_, nullptr);
//         pthread_mutex_init(&p_mutex_, nullptr);
//     }
//     void Push(const T &in) // 生产
//     {
//         P(pspace_sem_);

//         Lock(p_mutex_); // ?
//         ringqueue_[p_step_] = in;
//         // 位置后移，维持环形特性
//         p_step_++;
//         p_step_ %= cap_;
//         Unlock(p_mutex_); 

//         V(cdata_sem_);

//     }
//     void Pop(T *out)       // 消费
//     {
//         P(cdata_sem_);

//         Lock(c_mutex_); // ?
//         *out = ringqueue_[c_step_];
//         // 位置后移，维持环形特性
//         c_step_++;
//         c_step_ %= cap_;
//         Unlock(c_mutex_); 

//         V(pspace_sem_);
//     }
//     ~RingQueue()
//     {
//         sem_destroy(&cdata_sem_);
//         sem_destroy(&pspace_sem_);

//         pthread_mutex_destroy(&c_mutex_);
//         pthread_mutex_destroy(&p_mutex_);
//     }
// private:
//     std::vector<T> ringqueue_;
//     int cap_;

//     int c_step_;       // 消费者下标
//     int p_step_;       // 生产者下标

//     sem_t cdata_sem_;  // 消费者关注的数据资源
//     sem_t pspace_sem_; // 生产者关注的空间资源

//     pthread_mutex_t c_mutex_;
//     pthread_mutex_t p_mutex_;
// };